package k8.primitive;

import k8.SceneNode;
import k8.Texture;
import k8.TexturedTriangles;
import k8.buffer.Slice;

public class Triangle extends SceneNode
{

	// Buffer Slices
	private Slice vertices;

	private Slice texcoords;

	private Slice colours;

	// Vertex coordinates
	private float x1, y1, z1, x2, y2, z2, x3, y3, z3;

	// Colour values
	private float r1, g1, b1, r2, g2, b2, r3, g3, b3;

	// Texture coordinates
	private float u1, v1, u2, v2, u3, v3;

	private TexturedTriangles owner;

	private boolean visible;

	/** Creates an instance of Triangle */
	public Triangle()
	{
		this.vertices = null;
		this.texcoords = null;
		this.owner = null;

		// Visible by default
		this.visible = true;

		// Colours default to bright white
		r1 = g1 = b1 = r2 = g2 = b2 = r3 = g3 = b3 = 1;
	}

	/**
	 * Sets the visibility.
	 * 
	 * @param visible
	 *            TRUE for visible; FALSE for invisible
	 */
	public void setVisible(boolean visible)
	{
		this.visible = visible;
		if (vertices == null)
			return;
		if (!visible)
		{
			vertices.clear();
			return;
		}
		transform();
	}

	/**
	 * Removes the triangle instance from further rendering.
	 * 
	 * Associated buffers and texture are lost. Once removed it can only be
	 * rendered again after re-assignment of texture with a call to
	 * setTexture().
	 * 
	 * If you only want to temporally hide the triangle, and not changing
	 * textures then using setVisible() is more efficient.
	 */
	public void discard()
	{
		if (owner != null)
		{
			owner.discard(this);
			owner = null;
			vertices.discard();
			texcoords.discard();
			colours.discard();
		}
	}

	/**
	 * Sets the local vertex coordinates.
	 */
	public void setVertices(float x1, float y1, float z1, float x2, float y2,
			float z2, float x3, float y3, float z3)
	{
		this.x1 = x1;
		this.y1 = y1;
		this.z1 = z1;
		this.x2 = x2;
		this.y2 = y2;
		this.z2 = z2;
		this.x3 = x3;
		this.y3 = y3;
		this.z3 = z3;
		transform();
	}

	/**
	 * Sets the Slice to write world vertex coordinates to.
	 */
	public void setVertices(Slice vertices)
	{
		this.vertices = vertices;
		transform();
	}

	/**
	 * Sets the texture coordinates.
	 */
	public void setTexture(float u1, float v1, float u2, float v2, float u3,
			float v3)
	{
		this.u1 = u1;
		this.v1 = v1;
		this.u2 = u2;
		this.v2 = v2;
		this.u3 = u3;
		this.v3 = v3;
		if (texcoords == null)
			return;
		texcoords.put(u1, v1, u2, v2, u3, v3);
	}

	/**
	 * Sets the Slice to write texture coordinates to.
	 */
	public void setTexture(Slice texcoords)
	{
		this.texcoords = texcoords;
		if (texcoords == null)
			return;
		texcoords.put(u1, v1, u2, v2, u3, v3);
	}

	/**
	 * Sets the colours of the vertices.
	 */
	public void setColours(float r1, float g1, float b1, float r2, float g2,
			float b2, float r3, float g3, float b3)
	{
		this.r1 = r1;
		this.g1 = g1;
		this.b1 = b1;
		this.r2 = r2;
		this.g2 = g2;
		this.b2 = b2;
		this.r3 = r3;
		this.g3 = g3;
		this.b3 = b3;
		if (colours == null)
			return;
		colours.put(r1, g1, b1, r2, g2, b2, r3, g3, b3);
	}

	/**
	 * Sets the Slice to write colours to.
	 */
	public void setColours(Slice colours)
	{
		this.colours = colours;
		if (colours == null)
			return;
		colours.put(r1, g1, b1, r2, g2, b2, r3, g3, b3);
	}

	/**
	 * Sets the Texture.
	 */
	public void setTexture(Texture texture)
	{
		// Do we even need to change owners
		if (owner != null && owner.getTexture() == texture)
			return;
		discard();
		owner = TexturedTriangles.getInstance(texture);
		owner.assign(this);
	}

	/**
	 * Gets the Texture.
	 */
	public Texture getTexture()
	{
		return owner.getTexture();
	}

	@Override
	protected void transform()
	{
		// Don't waste time transforming if not visible or have no vertices
		if (!visible || vertices == null)
			return;

		// Update the vertices of this Triangle using the transformation matrix
		vertices
				.put(
						(float) (x1 * this.T[0] + y1 * this.T[1] + z1
								* this.T[2] + this.T[3]),
						(float) (x1 * this.T[4] + y1 * this.T[5] + z1 * T[6] + this.T[7]),
						(float) (x1 * this.T[8] + y1 * this.T[9] + z1
								* this.T[10] + this.T[11]),
						(float) (x2 * this.T[0] + y2 * this.T[1] + z2
								* this.T[2] + this.T[3]),
						(float) (x2 * this.T[4] + y2 * this.T[5] + z2
								* this.T[6] + this.T[7]),
						(float) (x2 * this.T[8] + y2 * this.T[9] + z2
								* this.T[10] + this.T[11]),
						(float) (x3 * this.T[0] + y3 * this.T[1] + z3
								* this.T[2] + this.T[3]),
						(float) (x3 * this.T[4] + y3 * this.T[5] + z3
								* this.T[6] + this.T[7]),
						(float) (x3 * this.T[8] + y3 * this.T[9] + z3
								* this.T[10] + this.T[11]));
	}
}
